/**
 * Ingenius-Group S.A.S
 *
 * Project: SAU
 * File: JdoUtil.java
 * Date: 06/03/2011
 */

package com.ingenius.sau.server.persistence.jdo;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.jdo.Transaction;

import com.ingenius.sau.shared.util.filter.Comparison;
import com.ingenius.sau.shared.util.filter.Filter;

/**
 * Class <strong>JdoUtil</strong>
 * 
 * @author Jesus David
 * @version 0.1
 */
public class JdoUtil {

	private static Logger logger = Logger.getLogger(JdoUtil.class.getName());

	public static List<Object> createJDOQL(Class<?> clazz,
			List<Filter> filters, String orderBy) {

		List<Object> result = new ArrayList<Object>();
		String jdoql = "select from " + clazz.getName();
		String flag = "", order = "";
		if (filters != null) {
			int parameterCounter = 1;
			for (Filter filter : filters) {
				if (filter.isValid()) {
					String op = " && ";
					if (!jdoql.contains(" where ")) {
						op = " where ";
					}
					if (filter.getComparison() == Comparison.DIFFERENT) {
						jdoql += op + filter.getFieldName();
						jdoql += " != ";
						jdoql += ":param" + parameterCounter++;
						result.add(filter.getValue());

					} else if (filter.getComparison() == Comparison.EQUALS) {
						jdoql += op + filter.getFieldName();
						jdoql += " == ";
						jdoql += ":param" + parameterCounter++;
						result.add(filter.getValue());

					} else if (flag.isEmpty()
							|| flag.equals(filter.getFieldName())) {
						jdoql += op + filter.getFieldName();
						switch (filter.getComparison()) {
						case GREATER_THAN:
							jdoql += " > ";
							break;
						case GREATER_THAN_OR_EQUAL:
							jdoql += " >= ";
							break;
						case LESS_THAN:
							jdoql += " < ";
							break;
						default:
							jdoql += " <= ";
							break;
						}
						jdoql += ":param" + parameterCounter++;
						result.add(filter.getValue());
						flag = filter.getFieldName();
						order = " order by " + flag;

					}
				}
			}
		}
		if (orderBy != null && !orderBy.isEmpty()) {
			if (order.isEmpty() || (!flag.isEmpty() && orderBy.contains(flag))) {
				order = " order by " + orderBy;
			} else {
				order += ", " + orderBy;
			}
		}
		result.add(0, jdoql + order);
		return result;
	}

	public static Integer count(Class<?> clazz, List<Filter> filters) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		List<Object> objects = JdoUtil.createJDOQL(clazz, filters, null);
		String jdoql = (String) objects.remove(0);
		Object[] params = objects.toArray();
		try {
			Query query = pm.newQuery(jdoql);
			query.setResult("count(this)");
			if (params == null) {
				params = new Object[] { 0 };
			}
			return (Integer) query.executeWithArray(params);

		} catch (Exception e) {
			String msg = "Failed to count objects [query=" + jdoql + "]. ";
			logger.log(Level.SEVERE, msg, e);
			// TODO
			System.out.println(msg + e.getMessage());
			e.printStackTrace();

		} finally {
			pm.close();
		}
		return 0;
	}

	public static <T> Boolean delete(Class<T> clazz, String id) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Transaction tx = pm.currentTransaction();
		Boolean result = false;
		try {
			logger.info("Deleting object...");
			tx.begin();
			T object = pm.getObjectById(clazz, id);
			pm.deletePersistent(object);
			tx.commit();
			result = true;
			logger.fine("ok -> object deleted [id=" + id + "]");

		} catch (Exception e) {
			String msg = "Failed to delete object [class=" + clazz.getName()
					+ ", id=" + id + "]. ";
			logger.log(Level.SEVERE, msg, e);
			// TODO
			System.out.println(msg + e.getMessage());
			e.printStackTrace();

		} finally {
			if (tx.isActive()) {
				tx.rollback();
			}
			pm.close();
		}
		return result;
	}

	@SuppressWarnings("unchecked")
	public static <T> List<T> getRange(Class<T> clazz, List<Filter> filters,
			String orderBy, int start, int limit, String fetchGroup) {

		PersistenceManager pm = PMF.get().getPersistenceManager();
		List<T> result = new ArrayList<T>();
		List<Object> objects = JdoUtil.createJDOQL(clazz, filters, orderBy);
		String jdoql = (String) objects.remove(0);
		Object[] params = objects.toArray();
		try {
			if (fetchGroup != null && !fetchGroup.isEmpty()) {
				logger.info("Adding fetch group...");
				pm.getFetchPlan().addGroup(fetchGroup);
			}
			logger.info("Retrieving objects...");
			Query query = pm.newQuery(jdoql);
			if (start < 0) {
				start = 0;
			}
			if (limit <= start) {
				limit = start + 10;
			}
			if (params == null) {
				params = new Object[] { 0 };
			}
			query.setRange(start, limit);
			List<T> tmp = (List<T>) query.executeWithArray(params);
			result = (List<T>) pm.detachCopyAll(tmp);
			logger.fine("ok -> objects retrieved");
			// TODO
			System.out.println("======== QUERY ========");
			System.out.println(query.toString());

		} catch (Exception e) {
			String msg = "Failed to retrieve objects [query=" + jdoql + "]. ";
			logger.log(Level.SEVERE, msg, e);
			// TODO
			System.out.println(msg + e.getMessage());
			e.printStackTrace();

		} finally {
			pm.close();
		}
		return result;
	}

	public static <T> List<T> getRange(Class<T> clazz, List<Filter> filters,
			String orderBy, int start, int limit) {

		return getRange(clazz, filters, orderBy, start, limit, null);
	}

	public static <T> T get(Class<T> clazz, String id, String fetchGroup) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		T result = null;
		try {
			if (fetchGroup != null && !fetchGroup.isEmpty()) {
				pm.getFetchPlan().addGroup(fetchGroup);
			}
			T tmp = pm.getObjectById(clazz, id);
			result = pm.detachCopy(tmp);

		} catch (Exception e) {
			result = null;
			String msg = "Failed to get object [class=" + clazz.getName()
					+ ", id=" + id + "]. ";
			logger.log(Level.SEVERE, msg, e);
			// TODO
			System.out.println(msg + e.getMessage());
			e.printStackTrace();

		} finally {
			pm.close();
		}
		return result;
	}

	public static <T> T get(Class<T> clazz, String id) {
		return get(clazz, id, null);
	}

	public static <T> List<T> getByIds(Class<T> clazz, Collection<String> ids,
			String fetchGroup) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		List<T> result = null;
		try {
			if (fetchGroup != null && !fetchGroup.isEmpty()) {
				pm.getFetchPlan().addGroup(fetchGroup);
			}
			List<T> tmp = new ArrayList<T>();
			for (String id : ids) {
				tmp.add(pm.getObjectById(clazz, id));
			}
			result = (List<T>) pm.detachCopyAll(tmp);

		} catch (Exception e) {
			result = null;
			String msg = "Failed to get objects [class=" + clazz.getName()
					+ ", ids=" + ids + "]. ";
			logger.log(Level.SEVERE, msg, e);
			// TODO
			System.out.println(msg + e.getMessage());
			e.printStackTrace();

		} finally {
			pm.close();
		}
		return result;
	}

	public static <T> List<T> getByIds(Class<T> clazz, Collection<String> ids) {
		return getByIds(clazz, ids, null);
	}

}
